package com.zlz.open.ugs.chain;


import com.zlz.open.ugs.core.chain.Chain;
import com.zlz.open.ugs.core.exchange.Exchange;
import com.zlz.open.ugs.exchange.UgsExchange;
import com.zlz.open.ugs.core.module.Module;
import com.zlz.open.ugs.module.OAuthModule;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Author: zhulinzhong
 * @Date: 2024/5/30
 */
public class UgsChain implements Chain {

    private final Map<String, Module> workerMap;

    private UgsExchange exchange;

    private List<Module> modules;

    private UgsChain chain;

    private Module currentModule;

    public UgsChain(Map<String, Module> workerMap, List<Module> modules, Module currentModule, UgsChain chain) {
        this.workerMap = workerMap;
        this.modules = modules;
        this.currentModule = currentModule;
        this.chain = chain;
    }

    public UgsChain(UgsExchange exchange, Map<String, Module> workerMap, boolean isDefault) {
        this.exchange = exchange;
        this.workerMap = workerMap;
        this.chain = this;
        initWorkersChain(isDefault);
    }

    private void initWorkersChain(boolean isDefault) {
        this.modules = new ArrayList<>();
        if (isDefault) {
            this.modules.add(workerMap.get(OAuthModule.class.getName()));
        } else {
            // this.modules.add(workerMap.get(OAuthModule.class.getName()));
        }
        UgsChain ugsChain = initChain();
        this.chain = ugsChain.chain;
        this.currentModule = ugsChain.currentModule;
    }

    private UgsChain initChain() {
        UgsChain chain = new UgsChain(workerMap, modules, null, null);
        ListIterator<Module> iterator = modules.listIterator(modules.size());
        while (iterator.hasPrevious()) {
            chain = new UgsChain(workerMap, modules, iterator.previous(), chain);
        }
        return chain;
    }

    @Override
    public Mono<Void> work(Exchange exchange) {
        return Mono.defer(() -> this.currentModule != null ? invokeFilter(this.currentModule, chain, exchange) : handler(exchange));
    }

    private Mono<Void> invokeFilter(Module current, UgsChain chain, Exchange exchange) {
        String currentName = current.getClass().getName();
        return current.work(exchange, chain).checkpoint(currentName + " [DefaultWebFilterChain]");
    }

    private Mono<Void> handler(Exchange exchange) {
        return exchange.response().write();
    }

    public static class Wrapper {

        private final UgsExchange exchange;

        private final List<Module> modules;

        public Wrapper(UgsExchange exchange, List<Module> module) {
            this.exchange = exchange;
            this.modules = module;
        }

        public UgsChain wrapper(boolean isDefault) {
            Map<String, Module> workerMap = modules.stream().collect(Collectors.toMap((worker) -> worker.getClass().getName(), Function.identity()));
            return new UgsChain(exchange, workerMap, isDefault);
        }
    }
}
